/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// import { BenchmarkRunner } from "../../../utils/benchmarkTsSuite";
// declare function print(arg:string) : string;
import java.util.Arrays;
import java.util.Random;

class Obj {
    public double value = 0;
    Obj(double value) {
        this.value = value;
    }
}

class  NormalCall {
    public static int global_value = 0;
    public static int[] arr = {12,43,56,76,89,54,45,32,35,47,46,44,21,37,84};


    public static Obj[] GenerateFakeRandomObject() {
        Obj[] resource = new Obj[15];
        Arrays.fill(resource, new Obj(0));

        for (int i = 0; i < 15; i++) {
            double randomValue = Math.random() * (10) + 1;
            resource[i] = new Obj(randomValue);
        }
        return resource;
    }

    /***** Without parameters *****/
    public static Obj ParameterlessFoo() {
        Obj[] res = {new Obj(0), new Obj(0), new Obj(0), new Obj(0), new Obj(0)};
        Obj[] resources =GenerateFakeRandomObject();
        for (int i = 0; i < 200; i++) {
            res[i % 5] = resources[i % 15];
        }
        return res[1];
    }

    public static Integer[] GenerateFakeRandomInteger() {
        Integer[] resource = {12, 43, 56, 76, 89, 54, 45, 32, 35, 47, 46, 44, 21, 37, 84};
        return resource;
    }

    /***************** With parameters *****************/
    public static Integer Foo(Integer[] resources, int i, int i3, int resourcesLength) {
        if ((resources[i % i3 & (resourcesLength - 1)] & 1) == 0) {
            i3 += 1;
        } else {
            i3 += 2;
        }
        return i3;
    }

    public static double RunNormalCall() {
        int count = 10000000;
        int i3  = 1;
        Integer[] resources = GenerateFakeRandomInteger();
        double startTime = System.nanoTime();
        Function<Integer[], Integer, Integer, Integer, Integer> foo =  NormalCall::Foo;
        int resourcesLength = resources.length;
        for (int i = 0; i < count; i++) {
            i3 = foo.apply(resources, i, i3, resourcesLength);
        }
        double midTime = System.nanoTime();
        for (int i = 0; i < count; i++) {
        }
        double endTime = System.nanoTime();
        double time = ((midTime - startTime) - (endTime - midTime)) / 1000000;
        System.out.println("res: " + i3);
        System.out.println("Function Call - RunNormalCall:\t"+time+"\tms");
        return time;
    }



    // No parameter function call
     public static double RunParameterlessCall() {
        int count = 10000;
        global_value = 0;
         Obj i3  = new Obj(1);
         double startTime = System.nanoTime();
         Supplier<Obj> foo =  NormalCall::ParameterlessFoo;
        for (int i = 0; i < count; i++) {
            i3 = foo.apply();
        }
         double midTime = System.nanoTime();
        for (int i = 0; i < count; i++) {
        }
         double endTime = System.nanoTime();
         double time = ((midTime - startTime) - (endTime - midTime)) / 1000000;
         System.out.println("res: " + i3);
         System.out.println("Function Call - RunParameterlessCall:\t"+time+"\tms");
        return time;
    }



    /***************************** Default  parameters *****************************/
    public static int DefaultFoo(Integer[] resources, int i, int i3, int resourcesLength) {
        if ((resources[i % i3 & (resourcesLength - 1)] & 1) == 0) {
            i3 += 1;
        } else {
            i3 += 2;
        }
        return i3;
    }

    public static double RunNormalDefCall() {
        int count = 10000000;
        global_value = 0;
        int i3 = 1;
        Integer[] resources  = GenerateFakeRandomInteger();
        int resourcesLength = resources.length;
        double startTime = System.nanoTime();
        Function<Integer[], Integer, Integer, Integer, Integer> foo = NormalCall::DefaultFoo;
        for (int i = 0; i < count; i++) {
            i3 = foo.apply(resources, i, i3, resourcesLength);
        }
        double midTime = System.nanoTime();
        for (int i = 0; i < count; i++) {
        }
        double endTime = System.nanoTime();
        double time = ((midTime - startTime) - (endTime - midTime)) / 1000000;
        System.out.println("res: " + i3);
        System.out.println("Function Call - RunNormalDefCall:\t"+time+"\tms");
        return time;
    }

    /********************* Different  parameters *********************/
    public static double DifferentFoo(Integer[] resources, int i, double i3, int resourcesLength) {
        if ((resources[(int)(i % i3) & (resourcesLength - 1)] & 1) == 0) {
            i3 += 1.1;
        } else {
            i3 += 2.1;
        }
        return i3;
    }

    public  static  double RunNormalDifferentCall() {
        int count = 10000000;
        global_value = 0;
        double i3  = 1.1;
        Integer[] resources = GenerateFakeRandomInteger();
        int resourcesLength = resources.length;
        double startTime = System.nanoTime();
        Function<Integer[], Integer, Double, Integer, Double> foo = NormalCall::DifferentFoo;
        for (int i = 0; i < count; i++) {
            i3 = foo.apply(resources, i, i3, resourcesLength);
        }
        double midTime = System.nanoTime();
        for (int i = 0; i < count; i++) {
        }
        double endTime = System.nanoTime();
        double time = ((midTime - startTime) - (endTime - midTime)) / 1000000;
        System.out.println("res: " + i3);
        System.out.println("Function Call - RunNormalDifferentCall:\t"+time+"\tms");
        return time;
    }

    /************************* Variable  parameters *************************/
    public static void VariableFoo(int a, String b, boolean c) {
        arr[global_value] += 1;
    }

    public static double RunNormalVariableFCall() {
        int count = 10000000;
        global_value = 1;
        double startTime = System.nanoTime();
        TriConsumer<Integer, String, Boolean> foo = NormalCall::VariableFoo;
        for (int i = 0; i < count; i++) {
            foo.apply(1, "1", true);
        }
        double midTime = System.nanoTime();
        for (int i = 0; i < count; i++) {
        }
        double endTime = System.nanoTime();
        double time = ((midTime - startTime) - (endTime - midTime)) / 1000000;
        System.out.println("Function Call - RunNormalVariableFCall:\t"+time+"\tms");
        return time;
    }
}

class Benchmark {
    public static void main(String[] args) {
        NormalCall.RunNormalCall();
        NormalCall.RunParameterlessCall();
        NormalCall.RunNormalDefCall();
        NormalCall.RunNormalDifferentCall();
        NormalCall.RunNormalVariableFCall();
        // System.out.println("Ts Method Call Is End, global_value value: \t"+global_value);
    }
}

@FunctionalInterface
interface Function<T, U, V, W, R> {
    R apply(T t, U u, V v, W w);
}

@FunctionalInterface
interface TriConsumer<T, U, V> {
    void apply(T t, U u, V v);
}

@FunctionalInterface
interface Supplier<R> {
    R apply();
}